package com.wsp.service.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import com.wsp.service.common.Const;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Package: com.wsp.service.Util
 * Description： TODO
 *
 * @Author: 巫思平
 * Copyright: Copyright (c) 2020
 * Version: 1
 */
@Component("redisUtil")
public final class RedisUtil {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    //expire time
    public boolean expire(String key,long time){
        return Boolean.TRUE.equals(redisTemplate.expire(key, time, TimeUnit.MILLISECONDS));
    }

    public boolean expire(String key) {
        return expire(key,Const.expireTime);
    }

    public boolean expire(String key, long time, TimeUnit timeUnit) {
        return Boolean.TRUE.equals(redisTemplate.expire(key,time,timeUnit));
    }

    public long getExpire(String key){
        try {
            return redisTemplate.getExpire(key, TimeUnit.MILLISECONDS);
        } catch (NullPointerException e) {
            return -2;
        }
    }

    //string
    public boolean set(String key,Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

    public boolean set(String key,Object value,long time, TimeUnit timeUnit){
        try {
            if(time>0){
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            }else{
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public Object get(String key){
        return key==null?null:redisTemplate.opsForValue().get(key);
    }

    public boolean setNotExit(String key, String value) {
        return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, Const.expireTime, TimeUnit.SECONDS));
    }

    public void remove(String key) {
        redisTemplate.delete(key);
    }
    //set
    public long sSet(String key, Object...values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    public Set<Object> sGet(String key){
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public long sRemove(String key, Object ...values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    public long sGetSetSize(String key){
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    //Zset
    public boolean zadd(String key, Object value, double score){
        try {
            redisTemplate.opsForZSet().add(key, value, score);
            return true;
        } catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    public boolean zIncrememt(String key, Object value, double score){
        try {
            redisTemplate.opsForZSet().incrementScore(key, value, score);
            return true;
        } catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    public Set<Object> zGet(String key){
        return redisTemplate.opsForZSet().range(key,0,-1);
    }

    @SuppressWarnings("unchecked")
    public Set<Object> zGetBiggerScore(String key, double score){
        //lua脚本
        List<String> keys = new ArrayList<>();
        keys.add(key);
        keys.add(""+score);
        DefaultRedisScript<Set> redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(Set.class);
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/getBiggerScore.lua")));
        return redisTemplate.execute(redisScript,new FastJsonRedisSerializer<>(String.class),new MyFastJsonRedisSerializer<>(Set.class), keys, (Object) null);
    }

    public void zRemove(String key, String value){
        redisTemplate.opsForZSet().remove(key, value);
    }
    //hash
    public boolean hset(String key,String item,Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public Object hget(String key,String item){
        return redisTemplate.opsForHash().get(key, item);
    }

    public Map<Object, Object> hmget(String key){
        return redisTemplate.opsForHash().entries(key);
    }

    public void hdel(String key, Object... item){
        redisTemplate.opsForHash().delete(key,item);
    }


    public long incr(String key, long delta){
        return redisTemplate.opsForValue().increment(key, delta);
    }

    public long decr(String key, long delta){
        return incr(key, -delta);
    }

    private static class MyFastJsonRedisSerializer<T> implements RedisSerializer<T> {
        private FastJsonConfig fastJsonConfig = new FastJsonConfig();
        private final Class<T> type;

        public MyFastJsonRedisSerializer(Class<T> type) {
            this.type = type;
        }

        public FastJsonConfig getFastJsonConfig() {
            return fastJsonConfig;
        }

        public void setFastJsonConfig(FastJsonConfig fastJsonConfig) {
            this.fastJsonConfig = fastJsonConfig;
        }

        @Override
        public byte[] serialize(T t) throws SerializationException {
            if (t == null) {
                return new byte[0];
            }
            try {
                return JSON.toJSONBytesWithFastJsonConfig(
                        fastJsonConfig.getCharset(),
                        t,
                        fastJsonConfig.getSerializeConfig(),
                        fastJsonConfig.getSerializeFilters(),
                        fastJsonConfig.getDateFormat(),
                        JSON.DEFAULT_GENERATE_FEATURE,
                        fastJsonConfig.getSerializerFeatures()
                );
            } catch (Exception ex) {
                throw new SerializationException("Could not serialize: " + ex.getMessage(), ex);
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public T deserialize(byte[] bytes) throws SerializationException {
            if (bytes == null || bytes.length == 0) {
                return null;
            }
            try {
                return (T) JSON.parseObject(
                        bytes,
                        fastJsonConfig.getCharset(),
                        type,
                        fastJsonConfig.getParserConfig(),
                        fastJsonConfig.getParseProcess(),
                        JSON.DEFAULT_PARSER_FEATURE,
                        fastJsonConfig.getFeatures()
                );
            } catch (Exception ex) {
                return null;
            }
        }
    }
}
